/*
 * SVMClass.cpp
 *
 *  Created on: Dec 23, 2013
 *      Author: thanhkm
 */

#include "Learning.h"

Learning::Learning() {
}

Learning::~Learning() {
}

cv::Mat Learning::mlp(cv::Mat& trainingData,
		cv::Mat& trainingClasses, cv::Mat& testData, cv::Mat& testClasses) {

	int nbDes = trainingData.cols;
	cv::Mat layers = cv::Mat(3, 1, CV_32SC1);

	layers.row(0) = cv::Scalar(nbDes);
	layers.row(1) = cv::Scalar(nbDes);
	layers.row(2) = cv::Scalar(1);

	CvANN_MLP mlp;
	CvANN_MLP_TrainParams params;
	CvTermCriteria criteria;
	criteria.max_iter = 100;
	criteria.epsilon = 0.00001f;
	criteria.type = CV_TERMCRIT_ITER | CV_TERMCRIT_EPS;
	params.train_method = CvANN_MLP_TrainParams::BACKPROP;
	params.bp_dw_scale = 0.05f;
	params.bp_moment_scale = 0.05f;
	params.term_crit = criteria;

//	mlp.create(layers, CvANN_MLP::SIGMOID_SYM);
	mlp.create(layers);
	// train
	mlp.train(trainingData, trainingClasses, cv::Mat(), cv::Mat(), params);

	cv::Mat response(1, 1, CV_32FC1);
	cv::Mat predicted(testClasses.rows, 1, CV_32F);
	for(int i = 0; i < testData.rows; i++) {
		cv::Mat response(1, 1, CV_32FC1);
		cv::Mat sample = testData.row(i);

		mlp.predict(sample, response);
		predicted.at<float>(i,0) = response.at<float>(0,0);

	}
	return predicted;
}

cv::Mat Learning::knn(cv::Mat& trainingData,
		cv::Mat& trainingClasses, cv::Mat& testData, cv::Mat& testClasses, int K) {

	CvKNearest knn(trainingData, trainingClasses, cv::Mat(), false, K);
	cv::Mat predicted(testClasses.rows, 1, CV_32F);
	for(int i = 0; i < testData.rows; i++) {
    		const cv::Mat sample = testData.row(i);
    		predicted.at<float>(i,0) = knn.find_nearest(sample, K);
    }
	return predicted;
}

cv::Mat Learning::bayes(cv::Mat& trainingData,
		cv::Mat& trainingClasses, cv::Mat& testData, cv::Mat& testClasses) {

	CvNormalBayesClassifier bayes(trainingData, trainingClasses);
	cv::Mat predicted(testClasses.rows, 1, CV_32F);
	for (int i = 0; i < testData.rows; i++) {
		const cv::Mat sample = testData.row(i);
		predicted.at<float> (i, 0) = bayes.predict(sample);
	}
	return predicted;
}

cv::Mat Learning::decisiontree(cv::Mat& trainingData,
		cv::Mat& trainingClasses, cv::Mat& testData, cv::Mat& testClasses) {

	CvDTree dtree;
	cv::Mat var_type(3, 1, CV_8U);

	// define attributes as numerical
	var_type.at<unsigned int>(0,0) = CV_VAR_NUMERICAL;
	var_type.at<unsigned int>(0,1) = CV_VAR_NUMERICAL;
	// define output node as numerical
	var_type.at<unsigned int>(0,2) = CV_VAR_NUMERICAL;

	dtree.train(trainingData,CV_ROW_SAMPLE, trainingClasses, cv::Mat(), cv::Mat(), var_type, cv::Mat(), CvDTreeParams());
	cv::Mat predicted(testClasses.rows, 1, CV_32F);
	for (int i = 0; i < testData.rows; i++) {
		const cv::Mat sample = testData.row(i);
		CvDTreeNode* prediction = dtree.predict(sample);
		predicted.at<float> (i, 0) = prediction->value;
	}
	return predicted;
}


cv::Mat Learning::svm(cv::Mat& trainingData, cv::Mat& trainingClasses,
						cv::Mat& testData, cv::Mat& testClasses) {
	// Set up SVM's parameters
	CvSVMParams params;
	params.svm_type = CvSVM::C_SVC;
	params.kernel_type = CvSVM::RBF;
	params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);

	time_t timer1, timer2, timer3;
	double seconds;
	time(&timer1);  // get current time; same as: timer = time(NULL)
	// SVM training
	CvSVM svm;
	svm.train(trainingData, trainingClasses, cv::Mat(), cv::Mat(), params);
//	svm.train(trainingData, trainingClasses, cv::Mat(), cv::Mat(), CvSVMParams());
//	svm.train_auto(trainingData, trainingClasses, cv::Mat(), cv::Mat(), CvSVMParams());

	time(&timer2);  // get current time; same as: timer = time(NULL)
	seconds = difftime(timer2, timer1);
	std::cout << "time of training: " << seconds << "s" << std::endl;

	cv::Mat predicted(testClasses.rows, 1, CV_32F);
	for(int i = 0; i < testData.rows; i++) {
		cv::Mat sample = testData.row(i);
		predicted.at<float>(i, 0) = svm.predict(sample);
	}
	time(&timer3);
	seconds = difftime(timer3, timer2);
	std::cout << "time of recognition: " << seconds << "s" << std::endl;

	return predicted;
}


cv::Mat Learning::convertToMatData(vector<classImage> data){
	int rows = data.size();
	int length_descripteurs = data[0].signature.size();
	cv::Mat matData(rows, length_descripteurs, CV_32FC1);

	for (int number = 0; number < 10; number++){
		for (int i = 0; i < rows; i++) {
			if (data[i].chiffreReel == number) {
				std::vector<double> signature = data[i].signature;
				for (int d = 0; d < length_descripteurs; d++) {
					matData.at<float>(i, d) = signature[d];
				}
			}
		}
	}
std::cout << "convert return size: " << matData.size() << std::endl;
	return matData;
}

cv::Mat Learning::convertToMatClasse(vector<classImage> data) {
	int rows = data.size();
	cv::Mat labels(rows, 1, CV_32FC1);

	for (int number = 0; number < 10; number++){
		for (int i = 0; i < rows; i++) {
			if (data[i].chiffreReel == number) {
				labels.at<float>(i, 0) = data[i].chiffreReel;
			}
		}
	}
	return labels;
}


// accuracy
float Learning::evaluate(cv::Mat& predicted, cv::Mat& actual) {
	assert(predicted.rows == actual.rows);
	int t = 0;
	int f = 0;
	for(int i = 0; i < actual.rows; i++) {
		float p = predicted.at<float>(i,0);
		float a = actual.at<float>(i,0);
		if(p == a) {
			t++;
		} else {
			f++;
			std::cout << p << " " << a << std::endl;
		}
	}
	std::cout << "good: " << t << " bad: " << f << std::endl;
	std::cout << "taux: " << t * 1.0 / (t + f) << std::endl;
	return (t * 1.0) / (t + f);
}
